package cn.anzhiyublog.staranimegallery.util;

import cn.anzhiyublog.staranimegallery.config.COSConfig;
import cn.anzhiyublog.staranimegallery.exception.GalleryException;
import cn.anzhiyublog.staranimegallery.model.domain.Picture;
import cn.anzhiyublog.staranimegallery.model.dto.picture.PictureUploadResult;
import cn.anzhiyublog.staranimegallery.model.enums.StatusCode;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpStatus;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.exception.CosClientException;
import com.qcloud.cos.exception.CosServiceException;
import com.qcloud.cos.model.PutObjectRequest;
import com.qcloud.cos.model.PutObjectResult;
import com.qcloud.cos.model.ciModel.persistence.CIObject;
import com.qcloud.cos.model.ciModel.persistence.ImageInfo;
import com.qcloud.cos.model.ciModel.persistence.PicOperations;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * COS图片上传下载工具
 *
 * @author 安知一只鱼
 */
@Component
@Slf4j
public class COSUtils {
    @Resource
    private COSConfig cosConfig;
    @Resource
    private COSClient cosClient;

    /**
     * 图片上传操作
     * @param pathKey 对象键
     * @param pictureFile 图片文件
     * @return PutObjectResult 上传结果
     */
    public PutObjectResult putPicture(String pathKey, File pictureFile){
        // 创建上传请求对象（参数：桶、对象键、图片文件）
        PutObjectRequest putPictureRequest = new PutObjectRequest(cosConfig.getBucket(), pathKey, pictureFile);
        // 创建 PicOperations 图片操作对象，用于设置图片操作相关的配置
        PicOperations picOperations = new PicOperations();
        // 设置规则
        ArrayList<PicOperations.Rule> rules = new ArrayList<>();
        // 规则1 图片类型转换(减少存储以降低流量消耗)：all->webp
        PicOperations.Rule compressRule = new PicOperations.Rule();
        compressRule.setBucket(cosConfig.getBucket());
        compressRule.setRule("imageMogr2/format/webp");
        compressRule.setFileId(FileUtil.mainName(pathKey)+".webp"); // xxx.webp
        rules.add(compressRule);
        // 规则2 图片缩略图
        PicOperations.Rule thumbnailRule = new PicOperations.Rule();
        thumbnailRule.setBucket(cosConfig.getBucket());
        thumbnailRule.setRule(String.format("imageMogr2/thumbnail/%sx%s",128,128)); // 如果大于大于原图则不处理
        thumbnailRule.setFileId(FileUtil.mainName(pathKey)+"_thumbnail."+FileUtil.getSuffix(pathKey));
        rules.add(thumbnailRule);
        // 完成规则设置
        picOperations.setRules(rules);
        // 设置消息返回（1表示获取图片信息，0不获取）
        picOperations.setIsPicInfo(1);
        // 将图片操作配置设置到上传请求中
        putPictureRequest.setPicOperations(picOperations);
        // 执行上传操作，返回上传结果
        return cosClient.putObject(putPictureRequest);
    }

    /**
     * 通过文件上传图片
     */
    public PictureUploadResult uploadPictureByFile(String pathPrefix,MultipartFile pictureFile) {
        // 文件格式校验
        this.validFile(pictureFile);
        // 构建上传路径
        String originalFilename = pictureFile.getOriginalFilename();
        String uploadPath = this.buildPath(pathPrefix, FileUtil.getSuffix(originalFilename));
        // 开始上传
        File file = null;
        try{
            // 创建临时文件
            file = File.createTempFile(uploadPath, null);
            pictureFile.transferTo(file);
            // 上传到cos
            PutObjectResult putPictureResult = this.putPicture(uploadPath, file);
            // 获取规则中的上传信息（如果成功得到压缩图和缩略图就设置压缩图信息）
            List<CIObject> ciObjects = putPictureResult.getCiUploadResult().getProcessResults().getObjectList();
            if (CollUtil.isNotEmpty(ciObjects)){
                CIObject compressedCiObject = ciObjects.get(0);
                CIObject thumbnailciObject = ciObjects.get(1);
                // 构造结果并返回
                return this.buildResult(originalFilename, compressedCiObject, thumbnailciObject);
            }
            // 获取上传信息（如果没有成功得到压缩图和缩略图就设置原图信息）
            ImageInfo imageInfo = putPictureResult.getCiUploadResult().getOriginalInfo().getImageInfo();
            // 构造结果并返回
            return this.buildResult(originalFilename, uploadPath, file, imageInfo);
        } catch (IOException e) {
            log.error("COS图片上传失败：{}", e.getMessage());
            throw new GalleryException(StatusCode.INTERNAL_SERVER_ERROR, "图片上传失败");
        } finally {
            this.deleteTempFile(file);
        }
    }

    /**
     * 通过URL上传图片
     */
    public PictureUploadResult uploadPictureByUrl(String pathPrefix,String url) {
        // 文件格式校验
        String format = this.validFile(url);
        // 构建上传路径
        String originalFilename = FileUtil.mainName(url)+"."+format;
        String uploadPath = this.buildPath(pathPrefix, FileUtil.getSuffix(originalFilename));
        // 开始上传
        File file = null;
        try{
            // 创建临时文件
            file = File.createTempFile(uploadPath, null);
            HttpUtil.downloadFile(url,file);
            // 上传到cos
            PutObjectResult putPictureResult = this.putPicture(uploadPath, file);
            // 获取规则中的上传信息（如果成功得到压缩图和缩略图就设置压缩图信息）
            List<CIObject> ciObjects = putPictureResult.getCiUploadResult().getProcessResults().getObjectList();
            if (CollUtil.isNotEmpty(ciObjects)){
                CIObject compressedCiObject = ciObjects.get(0);
                CIObject thumbnailciObject = ciObjects.get(1);
                // 构造结果并返回
                return this.buildResult(originalFilename, compressedCiObject, thumbnailciObject);
            }
            // 获取上传信息（如果没有成功得到压缩图和缩略图就设置原图信息）
            ImageInfo imageInfo = putPictureResult.getCiUploadResult().getOriginalInfo().getImageInfo();
            // 构造结果并返回
            return this.buildResult(originalFilename, uploadPath, file, imageInfo);
        } catch (IOException e) {
            log.error("COS图片上传失败：{}", e.getMessage());
            throw new GalleryException(StatusCode.INTERNAL_SERVER_ERROR, "图片上传失败");
        } finally {
            this.deleteTempFile(file);
        }
    }

    /**
     * 删除图片操作
     */
    public void deletePicture(String key) {
        // 空校验
        ThrowUtils.throwIf(key == null || key.isEmpty(), StatusCode.BAD_REQUEST, "删除失败：文件key为空");
        // 执行删除操作
        try {
            cosClient.deleteObject(cosConfig.getBucket(), key);
            log.info("成功删除COS文件：{}", key);
        } catch (CosServiceException e) {
            String errorMsg = e.getErrorCode().equals("NoSuchKey") ? "图片不存在" : e.getMessage();
            log.error("COS图片删除失败：{}", errorMsg);
        } catch (CosClientException e) {
            log.error("COS客户端异常：{}", e.getMessage());
        }
    }

    /**
     * 通过url删除
     */
    public void deletePictureByUrl(String url) {
        // 空校验
        ThrowUtils.throwIf(url == null || url.isEmpty(), StatusCode.BAD_REQUEST, "删除失败：文件url为空");
        // 拆分字符串：删除前面的host
        String key = StrUtil.replace(url, cosConfig.getHost(), "");
        // 执行删除操作
        this.deletePicture(key);
    }

    /**
     * 删除原图
     */
    public void deleteOriginalPicture(Picture picture) {
        // 空校验
        ThrowUtils.throwIf(picture == null, StatusCode.BAD_REQUEST, "删除失败：图片为空");
        ThrowUtils.throwIf(picture.getUrl()==null, StatusCode.BAD_REQUEST, "删除失败：图片url为空");
        ThrowUtils.throwIf(picture.getThumbnailUrl()==null, StatusCode.BAD_REQUEST, "删除失败：图片缩略图url为空");
        // 删除原图
        String key = "/ZhiyuStarAnimeGallery/public/" + picture.getUserId() + "/" + FileUtil.mainName(picture.getUrl()) + "." + FileUtil.getSuffix(picture.getThumbnailUrl());
        this.deletePicture(key);
    }

    /**
     * 通过Picture对象进行删除
     */
    public void deletePictureByObject(Picture picture) {
        // 空校验
        ThrowUtils.throwIf(picture == null, StatusCode.BAD_REQUEST, "删除失败：图片为空");
        ThrowUtils.throwIf(picture.getUrl()==null, StatusCode.BAD_REQUEST, "删除失败：图片url为空");
        ThrowUtils.throwIf(picture.getThumbnailUrl()==null, StatusCode.BAD_REQUEST, "删除失败：图片缩略图url为空");
        // 删除webp图
        this.deletePictureByUrl(picture.getUrl());
        // 删除缩略图
        this.deletePictureByUrl(picture.getThumbnailUrl());
        // 删除原图
        this.deleteOriginalPicture(picture);
    }

    /**
     * 构建上传路径
     */
    private String buildPath(String pathPrefix, String format){
        // 时间 + 16位随机字符串 + .格式，如 2025-01-14_6lrapxkBPzNwA0Kw.png
        String cosFilename = String.format("%s_%s.%s", DateUtil.formatDate(new Date()), RandomUtil.randomString(16), format);
        // 合并上传路径：/xxx/public/用户id/图片文件名
        return String.format("/%s/%s", pathPrefix, cosFilename);
    }

    /**
     * 构建图片上传结果
     */
    private PictureUploadResult buildResult(String name, String url, String thumbnailUrl,
                                            long size, int width, int height, String format){
        PictureUploadResult pictureUploadResult = new PictureUploadResult();
        pictureUploadResult.setName(name);
        pictureUploadResult.setUrl(cosConfig.getHost()+url);
        pictureUploadResult.setThumbnailUrl(cosConfig.getHost()+thumbnailUrl);
        pictureUploadResult.setPictureSize(size);
        pictureUploadResult.setPictureWidth(width);
        pictureUploadResult.setPictureHeight(height);
        pictureUploadResult.setPictureScale(NumberUtil.round(width * 1.0 / height, 2).doubleValue());
        pictureUploadResult.setPictureFormat(format);
        return pictureUploadResult;
    }
    private PictureUploadResult buildResult(String originalFilename,String uploadPath,File file,ImageInfo imageInfo){
        String name = FileUtil.mainName(originalFilename);
        long size = FileUtil.size(file);
        int width = imageInfo.getWidth();
        int height = imageInfo.getHeight();
        String format = imageInfo.getFormat();
        return this.buildResult(name, uploadPath,null,size,width,height,format);
    }
    private PictureUploadResult buildResult(String originalFilename, CIObject compressedCiObject, CIObject thumbnailCiObject){
        String name = FileUtil.mainName(originalFilename);
        String url = compressedCiObject.getKey();
        String thumbnailUrl = thumbnailCiObject.getKey();
        long size = compressedCiObject.getSize().longValue();
        int width = compressedCiObject.getWidth();
        int height = compressedCiObject.getHeight();
        String format = compressedCiObject.getFormat();
        return this.buildResult(name,url,thumbnailUrl,size,width,height,format);
    }


    /**
     * 删除临时文件
     */
    private void deleteTempFile(File file) {
        if (file != null) {
            // 删除临时文件
            boolean isDelete = file.delete();
            if (!isDelete) {
                log.error("图片删除失败，路径 = {}", file.getAbsolutePath());
            }
        }
    }

    /**
     * 文件校验
     */
    private void validFile(MultipartFile file){
        // 空校验
        ThrowUtils.throwIf(file==null, StatusCode.BAD_REQUEST, "文件不能为空");
        // 大小校验 最大2MB
        final long MB = 1024*1024L;
        final long MAX_SIZE = 10*MB;
        ThrowUtils.throwIf(file.getSize()>MAX_SIZE, StatusCode.BAD_REQUEST, "文件大小不能超过10MB");
        // 格式校验
        final List<String> SUPPORT_FORMAT = Arrays.asList("jpg","jpeg","png","gif","webp");
        String format = FileUtil.getSuffix(file.getOriginalFilename());
        ThrowUtils.throwIf(!SUPPORT_FORMAT.contains(format), StatusCode.NOT_ACCEPTABLE, "不支持该格式");
    }
    private String validFile(String url){
        // 返回文件格式
        String format;
        // url空校验
        ThrowUtils.throwIf(url==null, StatusCode.BAD_REQUEST, "url不能为空");
        // url格式
        try {
            new URL(url);
        } catch (MalformedURLException e) {
            throw new GalleryException(StatusCode.BAD_REQUEST, "url地址格式错误");
        }
        // url协议校验
        ThrowUtils.throwIf(!(url.startsWith("https://") || url.startsWith("http://")), StatusCode.BAD_REQUEST, "仅支持http或https协议的url");
        // 发送Head请求获取图片信息
        HttpResponse httpResponse = null;
        try{
            // 发送 Head 请求
            httpResponse = HttpUtil.createRequest(Method.HEAD, url).execute();
            // 检查状态
            ThrowUtils.throwIf(httpResponse.getStatus()!=HttpStatus.HTTP_OK, StatusCode.BAD_REQUEST, "url地址错误");
            // 检查文件类型
            final List<String> SUPPORT_FORMAT = Arrays.asList("image/jpeg", "image/jpg", "image/png", "image/webp","image/gif");
            String type = httpResponse.header("Content-Type");
            ThrowUtils.throwIf(StrUtil.isBlank(type), StatusCode.BAD_REQUEST, "未找到该url图片文件");
            ThrowUtils.throwIf(!SUPPORT_FORMAT.contains(type.toLowerCase()), StatusCode.NOT_ACCEPTABLE, "不支持该格式");
            // 是符合要求的文件格式则预留并准备返回
            format=StrUtil.replace(type,"image/","");
            // 检查文件大小
            String sizeStr = httpResponse.header("Content-Length");
            final long MB = 1024 * 1024L;
            final long MAX_SIZE = 10 * MB;
            ThrowUtils.throwIf(StrUtil.isBlank(sizeStr), StatusCode.BAD_REQUEST, "未找到该url图片文件");
            long size = Long.parseLong(sizeStr);
            ThrowUtils.throwIf(size>MAX_SIZE, StatusCode.BAD_REQUEST, "文件大小不能超过10MB");
            return format;
        }finally {
            // 释放资源
            if (httpResponse!=null)
                httpResponse.close();
        }
    }
}
